package com.wanwu.channel.notify.biz;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;


import com.wanwu.channel.notify.biz.mapper.PayNotifyMapper;
import com.wanwu.channel.notify.biz.mapper.TradeOrderMapper;
import com.wanwu.channel.notify.biz.po.PayNotify;
import com.wanwu.channel.notify.biz.po.TradeOrder;
import com.wanwu.channel.notify.constants.CommonConstants;
import com.wanwu.channel.notify.model.PayNotifyReq;
import com.wanwu.channel.notify.model.TradeReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
public class TradeOrderService {
    @Autowired
    private TradeOrderMapper tradeOrderMapper;
    @Autowired
    private PayNotifyMapper payNotifyMapper;


    /**
     * 获取交易单
     * @param outTradeNo
     */
    public TradeOrder getTradeOrder(String outTradeNo){
        LambdaQueryWrapper<TradeOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TradeOrder::getOutTradeNo,outTradeNo);
        return tradeOrderMapper.selectOne(queryWrapper);
    }

    /**
     * 更新交易单状态
     * @param tradeOrder
     * @param tradeState
     */
    private void updateTradeOrder(TradeOrder tradeOrder,Integer tradeState){

        tradeOrder.setStatus(CommonConstants.TRADE_STATUS_FINISH);//交易完成
        //处理回调结果
        if (CommonConstants.TRADE_PAY_NOTIFY_RESULT_SUCCESS.equals(tradeState)) {
            //成功处理
            tradeOrder.setResult(CommonConstants.TRADE_PAY_RESULT_SUCCESS);
            tradeOrder.setUpdateTime(new Date());
        } else {
            //失败处理
            tradeOrder.setResult(CommonConstants.TRADE_PAY_RESULT_FAIL);
        }
        tradeOrderMapper.updateById(tradeOrder);
    }

    /**
     * 支付回调
     */
    public void payNotify(String wechatNotifyStr) throws Exception {
        Map<String, String> notifyParam = xmlToMap(wechatNotifyStr);
        String outTradeNo = notifyParam.get("out_trade_no");
        String tradeState = notifyParam.get("trade_state");
        BigDecimal totalFee = BigDecimal.valueOf(Double.parseDouble(notifyParam.get("total_fee")));
        TradeOrder tradeOrder = getTradeOrder(outTradeNo);
        if(Objects.isNull(tradeOrder)){
            throw new Exception("微信回调订单不存在");
        }
        //判断通知报文中总金额跟数据库是否一致
        if (!totalFee.equals(tradeOrder.getTotalFee())) {
            throw new Exception("微信支付回调订单金额与本地不一致");
        }
        updateTradeOrder(tradeOrder,Integer.parseInt(tradeState));
        PayNotify payNotify = new PayNotify().setTradeNo(tradeOrder.getTradeNo()).setPayResult(tradeOrder.getResult()).setTimes(0);
        payNotifyMapper.insert(payNotify);
    }


    /**
     * 同步交易状态
     * @param payNotifyReq
     * @throws Exception
     */
    @Transactional
    public void syncPayStatus(PayNotifyReq payNotifyReq) throws Exception {
        TradeOrder tradeOrder = getTradeOrder(payNotifyReq.getOutTradeNo());
        if(Objects.isNull(tradeOrder)){
            throw new Exception("订单号不存在");
        }

        if(CommonConstants.TRADE_TYRPE_GUARANTEE.equals(tradeOrder.getType())
           &&!CommonConstants.TRADE_DELIVERY_RESULT_SUCCESS.equals(tradeOrder.getResult())){
            throw new Exception("担保交易，只用收货成功后才能更新支付状态");
        }
        else if(!CommonConstants.TRADE_RESULT_INIT.equals(tradeOrder.getResult())){
            throw new Exception("交易订单已经完成");
        }
        updateTradeOrder(tradeOrder, payNotifyReq.getPayResult());
    }

    /**
     * 新增交易订单
     * @param tradeReq
     * @throws Exception
     */
    @Transactional
    public void saveTrade(TradeReq tradeReq) throws Exception {
        TradeOrder tradeOrder = getTradeOrder(tradeReq.getOutTradeNo());
        if(Objects.nonNull(tradeOrder)){
            throw new Exception("订单号已经存在");
        }
        tradeOrder = new TradeOrder();
        tradeOrder.setOutTradeNo(tradeReq.getOutTradeNo());
        tradeOrder.setMerchantCode(tradeReq.getMerchantCode());
        tradeOrder.setGoodsBody(tradeReq.getGoodsBody());
        tradeOrder.setGoodsTag(tradeReq.getGoodsTag());
        tradeOrder.setTotalFee(tradeReq.getTotalFee());
        tradeOrder.setNotifyUrl(tradeReq.getNotifyUrl());
        tradeOrder.setDetail(tradeReq.getDetail());
        tradeOrder.setType(tradeReq.getType());
        tradeOrder.setResult(0);
        tradeOrder.setStatus(2);
        tradeOrder.setExpireTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(tradeReq.getExpireTime()));

        tradeOrderMapper.insert(tradeOrder);
    }

    /**
     * mock调用微信查询订单状态
     */
    private Map<String,Object> mockInvokerWxQueryPayState(String outTradeNo,String mchid){
        Map<String,Object> map = new HashMap<>();
        map.put("out_trade_no","1217752501201407033233368018");
        map.put("trade_state","SUCCESS");
        return map;
    }


    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes(StandardCharsets.UTF_8));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            log.error("微信返回xml转map失败",ex);
            throw new Exception("微信返回xml转map失败");
        }

    }


    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }
    public Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
}

